package com.quiz.bis.service.impl;

import com.quiz.bis.domain.dto.QuestionDuplicateResult;
import com.quiz.bis.domain.model.ProcessedText;
import com.quiz.bis.domain.model.QuestionFingerprint;
import com.quiz.bis.mapper.QuestionFingerprintMapper;
import com.quiz.bis.service.ITextSimilarityService;
import com.quiz.bis.service.impl.ImageHashService;
import com.quiz.bis.util.text.CosineSimilarity;
import com.quiz.bis.util.text.SimHash;
import com.quiz.bis.util.text.TextPreprocessor;
import com.quiz.file.domain.model.FileInfo;
import com.quiz.file.mapper.FileMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文本相似度服务实现
 */
@Service
@RequiredArgsConstructor
public class TextSimilarityServiceImpl implements ITextSimilarityService {

    private final TextPreprocessor textPreprocessor;
    private final FileMapper fileMapper;
    private final QuestionFingerprintMapper questionFingerprintMapper;
    private final ImageHashService imageHashService;

    /**
     * 计算两个文本的相似度
     */
    @Override
    public double calculateSimilarity(String text1, String text2) {
        ProcessedText processed1 = textPreprocessor.preprocess(text1);
        ProcessedText processed2 = textPreprocessor.preprocess(text2);
        return calculateSimilarity(processed1, processed2);
    }

    /**
     * 计算两个预处理文本的相似度
     * 根据实际存在的特征类型动态分配权重
     */
    @Override
    public double calculateSimilarity(ProcessedText text1, ProcessedText text2) {
        // 基础权重配置
        final double BASE_TEXT_WEIGHT = 0.6;
        final double FORMULA_WEIGHT = 0.2;
        final double IMAGE_WEIGHT = 0.1;
        final double TABLE_WEIGHT = 0.1;

        // 计算各项相似度
        double textSimilarity = CosineSimilarity.computeCosineSimilarity(text1.getWords(), text2.getWords());
        double formulaSimilarity = calculateFormulaSimilarity(text1.getFormulas(), text2.getFormulas());
        double imageSimilarity = calculateImageSimilarity(text1.getImageIds(), text2.getImageIds());
        double tableSimilarity = calculateTableSimilarity(text1.getTables(), text2.getTables());

        // 确定哪些特征存在
        boolean hasFormulas = !text1.getFormulas().isEmpty() || !text2.getFormulas().isEmpty();
        boolean hasImages = !text1.getImageIds().isEmpty() || !text2.getImageIds().isEmpty();
        boolean hasTables = !text1.getTables().isEmpty() || !text2.getTables().isEmpty();

        // 计算实际权重总和
        double totalWeight = BASE_TEXT_WEIGHT;
        if (hasFormulas) {totalWeight += FORMULA_WEIGHT;}
        if (hasImages) {totalWeight += IMAGE_WEIGHT;}
        if (hasTables) {totalWeight += TABLE_WEIGHT;}

        // 计算加权和
        double weightedSum = BASE_TEXT_WEIGHT * textSimilarity;
        if (hasFormulas) {weightedSum += FORMULA_WEIGHT * formulaSimilarity;}
        if (hasImages) {weightedSum += IMAGE_WEIGHT * imageSimilarity;}
        if (hasTables) {weightedSum += TABLE_WEIGHT * tableSimilarity;}

        // 归一化处理
        return weightedSum / totalWeight;
    }

    /**
     * 计算公式相似度
     */
    public double calculateFormulaSimilarity(List<String> formulas1, List<String> formulas2) {
        if (formulas1.isEmpty() && formulas2.isEmpty()) {
            return 1.0;
        }
        if (formulas1.isEmpty() || formulas2.isEmpty()) {
            return 0.0;
        }

        // 使用Jaccard相似度计算公式相似度
        Set<String> set1 = new HashSet<>(formulas1);
        Set<String> set2 = new HashSet<>(formulas2);

        Set<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);

        Set<String> union = new HashSet<>(set1);
        union.addAll(set2);

        return union.isEmpty() ? 0.0 : (double) intersection.size() / union.size();
    }

    /**
     * 计算图片相似度
     */
    public double calculateImageSimilarity(List<Long> imageIds1, List<Long> imageIds2) {
        if (imageIds1.isEmpty() && imageIds2.isEmpty()) {
            return 1.0;
        }
        if (imageIds1.isEmpty() || imageIds2.isEmpty()) {
            return 0.0;
        }

        // 获取所有图片的哈希值
        Map<Long, String> hashes1 = getImageHashes(imageIds1);
        Map<Long, String> hashes2 = getImageHashes(imageIds2);

        // 计算最佳匹配相似度
        double totalSimilarity = 0;
        int comparisons = 0;

        for (Map.Entry<Long, String> entry1 : hashes1.entrySet()) {
            for (Map.Entry<Long, String> entry2 : hashes2.entrySet()) {
                double similarity = imageHashService.compareHashes(entry1.getValue(), entry2.getValue());
                totalSimilarity += similarity;
                comparisons++;
            }
        }

        return comparisons > 0 ? totalSimilarity / comparisons : 0.0;
    }

    private Map<Long, String> getImageHashes(List<Long> imageIds) {
        Map<Long, String> hashes = new HashMap<>();
        for (Long imageId : imageIds) {
            FileInfo fileInfo = fileMapper.selectById(imageId);
            if (fileInfo != null && fileInfo.getMd5() != null) {
                hashes.put(imageId, fileInfo.getMd5());
            }
        }
        return hashes;
    }

    /**
     * 计算表格相似度
     */
    public double calculateTableSimilarity(List<String> tables1, List<String> tables2) {
        if (tables1.isEmpty() && tables2.isEmpty()) {
            return 1.0;
        }
        if (tables1.isEmpty() || tables2.isEmpty()) {
            return 0.0;
        }

        // 简化的表格相似度计算 - 比较表格结构
        // 实际中可以更复杂，比如比较行列数、表头等
        Set<String> set1 = new HashSet<>(tables1);
        Set<String> set2 = new HashSet<>(tables2);

        Set<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);

        Set<String> union = new HashSet<>(set1);
        union.addAll(set2);

        return union.isEmpty() ? 0.0 : (double) intersection.size() / union.size();
    }

    /**
     * 计算文本的SimHash值
     */
    @Override
    public long computeSimHash(String text,ProcessedText processedText) {
//        ProcessedText processed = textPreprocessor.preprocess(text);
        return SimHash.computeSimHash(processedText.getWords());
    }

    /**
     * 计算文本的SimHash值
     */
    @Override
    public long computeSimHash(ProcessedText processedText) {
        return SimHash.computeSimHash(processedText.getWords());
    }

    /**
     * 计算文本的SimHash值
     */
    @Override
    public long computeSimHash(String text) {
        ProcessedText processed = textPreprocessor.preprocess(text);
        return SimHash.computeSimHash(processed.getWords());
    }

    /**
     * 查找相似题目
     */
    public List<QuestionDuplicateResult> findSimilarQuestions(String content, double threshold) {
        if(threshold < 0 || threshold > 1) {
            throw new IllegalArgumentException("threshold必须位于区间[0,1]");
        }
        ProcessedText processed = textPreprocessor.preprocess(content);
        long simHash = SimHash.computeSimHash(processed.getWords());

        // 先用SimHash筛选可能相似的题目
        List<QuestionFingerprint> candidates = questionFingerprintMapper.findSimilarBySimHash(
                simHash, 50);

        // 计算混合相似度
        return candidates.stream()
                .map(candidate -> {
                    ProcessedText candidateText = textPreprocessor.preprocess(candidate.getContent());
                    double similarity = calculateSimilarity(processed, candidateText);
                    return new QuestionDuplicateResult(
                            candidate.getId(),
                            candidate.getTitle(),
                            candidate.getContentText(),
                            candidate.getTypeId(),
                            similarity
                    );
                })
                .filter(result -> result.getSimilarity() >= threshold)
                .sorted((r1, r2) -> Double.compare(r2.getSimilarity(), r1.getSimilarity()))
                .collect(Collectors.toList());
    }
}